﻿using FictionsDownload.BLL;
using FictionsDownload.DAL;
using FictionsDownload.Model;
using FictionsDownload.Static;
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Schedulers;
using System.Windows.Forms;

namespace FictionsDownload.UI
{
    public partial class FormBookTree : Form
    {
        /// <summary>
        /// 鼠标点击TreeView记录相关信息
        /// </summary>
        private struct MouseNode
        {
            public int X;
            public int Y;
            public int Clicks;
            public ETreeNodeKeyType Type;
            public TreeNode Node;
            public string Id;
        }
        #region Enum
        /// <summary>
        /// 树节点类型:文件夹/书籍
        /// </summary>
        private enum ETreeNodeKeyType
        {
            文件夹 = 1,
            书籍,
        }

        #endregion
        #region 内部变量
        private BLL.Book book = new BLL.Book();
        private BLL.Folder folder = new BLL.Folder();
        private BackupAPI backup = new BackupAPI();
        //点击节点的信息
        MouseNode mouseNode = new MouseNode();
        //保存节点的开关状态
        private Hashtable nodesStatus = new Hashtable();
        //保存选中的节点路径
        private string selectedNodeFullPath;
        #endregion
        #region 公共属性和事件
        public Thread_BookDownload ThreadDownload { get; private set; }
        #endregion
        #region 内部属性
        private FormMain FormMain
        {
            get
            {
                if(this.MdiParent == null)
                {
                    //父窗体不是FormMain，是Panel
                    return (FormMain)this.Parent.Parent;
                }
                else
                {
                    return (FormMain)this.MdiParent;
                }
            }
        }
        #endregion

        #region 单例初始化
        private FormBookTree()
        {
            InitializeComponent();
            this.TopLevel = false;
            this.FormBorderStyle = FormBorderStyle.None;
            this.Show();
        }
        private static FormBookTree formInstance;
        private static readonly object lockRoot = new object();
        /// <summary>
        /// 初始化实例
        /// </summary>
        public static FormBookTree GetIntance
        {
            get
            {
                if (formInstance == null)
                {
                    lock(lockRoot)
                    {
                        if (formInstance == null)
                        {
                            formInstance = new FormBookTree();
                        }
                    }
                }
                return formInstance;
            }
        }
        #endregion

        #region 控件初始化
        public void InitializeComponentCode()
        {
            ThreadInit();
            tv_BooksInit();
        }
        private void ThreadInit()
        {
            ThreadDownload = Thread_BookDownload.GetIntance;
            ThreadDownload.ToDirectoryChanging += new EventHandler(ThreadToDirectoryChanging);
            ThreadDownload.ToDirectoryChanged += new EventHandler(ThreadToDirectoryChanged);
            ThreadDownload.DownLoadTextChanging += new EventHandler(ThreadDownLoadTextChanging);
            ThreadDownload.DownLoadTextChanged += new EventHandler(ThreadDownLoadTextChanged);
            ThreadDownload.StartDownload += new EventHandler(ThreadStartDownload);
            ThreadDownload.StopDownload += new EventHandler(ThreadStopDownload);
        }
        private void tv_BooksInit()
        {
            //pl_BookTree的底部和容器贴合。
            this.tv_Books.Size = new Size(this.tv_Books.Size.Width, this.ClientSize.Height - this.tv_Books.Location.Y);
            FormMain.BookTreeY = tv_Books.Location.Y;
            tv_BooksRefresh();
        }
        #endregion
        #region 事件
        private void ThreadToDirectoryChanging(object sender,EventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler(ThreadToDirectoryChanging), sender, e);
                return;
            }
            Thread_BookDownload thr = (Thread_BookDownload)sender;
            TreeNode treeNode = FindNodeByName(tv_Books.Nodes, ETreeNodeKeyType.书籍 + "_" + thr.DownloadingBook.Id.ToString());
            SetTreeNodeImage(ilist_Books.Images.IndexOfKey("bookdown.png"), treeNode);
        }
        /// <summary>
        /// 设置TreeNode以及父节点的图标
        /// </summary>
        /// <param name="imageIndex"></param>
        /// <param name="treeNode"></param>
        private void SetTreeNodeImage(int imageIndex,TreeNode treeNode)
        {
            treeNode.ImageIndex = imageIndex;
            treeNode.SelectedImageIndex = imageIndex;
            if (treeNode.Parent != null)
            {
                SetTreeNodeImage(imageIndex, treeNode.Parent);
                return;
            }
            return;
        }
        /// <summary>
        /// 根据书籍设置节点图标
        /// </summary>
        /// <param name="book"></param>
        /// <param name="treeNode"></param>
        private void SetTreeNodeImage(BookModel book, TreeNode treeNode)
        {
            if (book.State == BookModel.EState.正常更新)
            {
                treeNode.ImageIndex = ilist_Books.Images.IndexOfKey("Book_En.png");
                treeNode.SelectedImageIndex = ilist_Books.Images.IndexOfKey("Book_En_Open.png");
            }
            else
            {
                treeNode.ImageIndex = ilist_Books.Images.IndexOfKey("Book_Dis.png");
                treeNode.SelectedImageIndex = ilist_Books.Images.IndexOfKey("Book_Dis_Open.png");
            }
            //父级是文件夹，恢复文件夹图标
            if (treeNode.Parent != null)
            {
                SetTreeNodeImage(ilist_Books.Images.IndexOfKey("folder.png"), treeNode.Parent);
                return;
            }
        }
        private void ThreadToDirectoryChanged(object sender,EventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler(ThreadToDirectoryChanged), sender, e);
                return;
            }
            Thread_BookDownload thr = (Thread_BookDownload)sender;
            TreeNode treeNode = FindNodeByName(tv_Books.Nodes, ETreeNodeKeyType.书籍 + "_" + thr.DownloadingBook.Id.ToString());
            SetTreeNodeFont(thr.DownloadingBook, treeNode);
            SetTreeNodeImage(thr.DownloadingBook, treeNode);
        }
        private void ThreadDownLoadTextChanging(object sender, EventArgs e)
        {
            ThreadToDirectoryChanging(sender, e);
        }
        private void ThreadDownLoadTextChanged(object sender, EventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler(ThreadDownLoadTextChanged), sender, e);
                return;
            }
            Thread_BookDownload thr = (Thread_BookDownload)sender;
            TreeNode treeNode = FindNodeByName(tv_Books.Nodes, ETreeNodeKeyType.书籍 + "_" + thr.DownloadingBook.Id.ToString());
            SetTreeNodeImage(thr.DownloadingBook, treeNode);
        }
        private void btn_Download_Click(object sender, EventArgs e)
        {
            if (btn_Download.Text == "停止下载")
            {
                ThreadDownload.Stop();
            }
            else
            {
                ThreadDownload.Start(FolderModel.BaseNode, new FolderModel().GetType());
            }
        }
        private void btn_AddTitle_Click(object sender, EventArgs e)
        {
            FormMain.ShowFormBook(EditState.新建,FolderModel.BaseNode);
        }
        #endregion
        /// <summary>
        /// 获取指定Name的节点
        /// </summary>
        /// <param name="tnParent">指定节点</param>
        /// <param name="strValue">value值</param>
        /// <returns></returns>
        private TreeNode FindNodeByName(TreeNodeCollection treeNodes, string findName)
        {
            if (treeNodes == null) { return null; }
            TreeNode tempTn = null;
            foreach (TreeNode tn in treeNodes)
            {
                if (tn == null) { return null; }
                if (tn.Name == findName) { return tn; }
                tempTn = FindNodeByName(tn.Nodes, findName);
                if (tempTn != null)
                {
                    break;
                }
            }
            return tempTn;
        }

        /// <summary>
        /// 刷新TrreView
        /// </summary>
        /// <param name="dt"></param>
        public void tv_BooksRefresh()
        {
            List<FolderModel> folders = folder.GetList<FolderModel>().OrderBy(m=>m.SortIndex).ToList();
            List<BookModel> books = book.GetList<BookModel>();
            GetTreeNodesStatus(tv_Books.Nodes);
            //清空treeview上所有节点
            tv_Books.Nodes.Clear();
            tv_Books_ForAdd(folders, books, FolderModel.BaseNode, null);
            SetTreeNodesStatus(tv_Books.Nodes);
        }
        private void ThreadStartDownload(object sender,EventArgs e)
        {
            //外线程调用
            if (InvokeRequired)
            {
                Invoke(new EventHandler(ThreadStartDownload),sender,e);
                return;
            }
            btn_Download.Text = "停止下载";
        }
        private void ThreadStopDownload(object sender, EventArgs e)
        {
            //外线程调用
            if (InvokeRequired)
            {
                Invoke(new EventHandler(ThreadStopDownload), sender, e);
                return;
            }
            btn_Download.Text = "开始下载";
            Thread_BookDownload thr = (Thread_BookDownload)sender;
            TreeNode treeNode = FindNodeByName(tv_Books.Nodes, ETreeNodeKeyType.书籍 + "_" + thr.DownloadingBook.Id.ToString());
            SetTreeNodeFont(thr.DownloadingBook, treeNode);
            SetTreeNodeImage(thr.DownloadingBook, treeNode);
        }
        /// <summary>
        /// 循环将数据加入TreeView
        /// </summary>
        /// <param name="folders">准备添加的文件夹列表</param>
        /// <param name="books">准备添加的书籍列表</param>
        /// <param name="folderUUID">当前要加入的文件夹Id</param>
        /// <param name="treeNode">当前要加入的树节点</param>
        private void tv_Books_ForAdd(List<FolderModel> folders, List<BookModel> books, string folderUUID, TreeNode treeNode)
        {
            //查找出当前需要添加的节点，null是顶层节点
            List<FolderModel> ForFolders = folders.Where(i => i.FolderId == folderUUID).ToList();
            //下一个节点
            TreeNode treeNodeNext = new TreeNode();
            foreach (FolderModel folder in ForFolders)
            {
                if (treeNode == null)
                {
                    treeNodeNext = tv_Books.Nodes.Add(ETreeNodeKeyType.文件夹 + "_" + folder.Id.ToString(), folder.Name);
                }
                else
                {
                    treeNodeNext = treeNode.Nodes.Add(ETreeNodeKeyType.文件夹 + "_" + folder.Id.ToString(), folder.Name);
                }
                //添加节点图标
                treeNodeNext.ImageIndex = treeNodeNext.SelectedImageIndex = ilist_Books.Images.IndexOfKey("folder.png");
                //递归
                tv_Books_ForAdd(folders, books, folder.Id, treeNodeNext);
            }
            List<BookModel> Forbooks = books.Where(i => i.FolderId == folderUUID).ToList();
            foreach (BookModel book in Forbooks)
            {
                if (treeNode == null)
                {
                    treeNodeNext = tv_Books.Nodes.Add(ETreeNodeKeyType.书籍 + "_" + book.Id.ToString(), book.Name);
                }
                else
                {
                    treeNodeNext = treeNode.Nodes.Add(ETreeNodeKeyType.书籍 + "_" + book.Id.ToString(), book.Name);
                }
                //添加节点样式
                SetTreeNodeFont(book, treeNodeNext);
                //添加节点图标
                SetTreeNodeImage(book, treeNodeNext);
            }
        }
           /// <summary>
           /// 书籍有未读章节，就显示粗体和章节数
           /// </summary>
           /// <param name="book"></param>
           /// <param name="treeNode"></param>
        private void SetTreeNodeFont(BookModel book,TreeNode treeNode)
        {
            int dirCount = folder.All<Model.DirectoryModel>().Where(d => d.BookId == book.Id && d.State == true && d.Read != true).Count();
            if (dirCount > 0)
            {
                treeNode.NodeFont = new Font(tv_Books.Font, FontStyle.Bold);
                treeNode.Text = book.Name + " " + dirCount;
            }
        }
        /// <summary>
        /// 保存TreeNode状态
        /// </summary>
        /// <param name="nodes"></param>
        private void GetTreeNodesStatus(TreeNodeCollection nodes)
        {
            foreach (TreeNode node in nodes)
            {
                if (node.IsExpanded)
                {
                    nodesStatus[node.FullPath] = true;
                }
                else
                {
                    nodesStatus.Remove(node.FullPath);
                }
                if (node.IsSelected)
                {
                    selectedNodeFullPath = node.FullPath;
                }
                GetTreeNodesStatus(node.Nodes);
            }
        }

        /// <summary>
        /// 设置TreeNode状态
        /// </summary>
        /// <param name="nodes"></param>
        private void SetTreeNodesStatus(TreeNodeCollection nodes)
        {
            if (nodesStatus == null)
            {
                return;
            }
            foreach (TreeNode node in nodes)
            {
                if (nodesStatus[node.FullPath] != null)
                {
                    node.Expand();
                }
                if (node.FullPath == selectedNodeFullPath)
                {
                    tv_Books.SelectedNode = node;
                }
                SetTreeNodesStatus(node.Nodes);
            }
        }

        private void tv_Books_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                //右键双击不生效
                return;
            }
            if (mouseNode.X < mouseNode.Node.Bounds.Left || mouseNode.X > mouseNode.Node.Bounds.Right)
            {
                //不在区域内
                return;
            }
            //在区域内，响应处理
            if (mouseNode.Type == ETreeNodeKeyType.文件夹)
            {
                //文件夹，不做处理
                return;
            }
            else if (mouseNode.Type == ETreeNodeKeyType.书籍)
            {
                修改ToolStripMenuItem_Click(sender, e);
            }
            else
            {
                MessageBox.Show("异常：节点首字无法识别:" + mouseNode.Type.ToString());
            }
        }
        private void tv_Books_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
        {
            //双击操作，取消事件
            e.Cancel = (mouseNode.Clicks > 1);
        }
        private void tv_Books_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            //双击操作，取消事件
            e.Cancel = (mouseNode.Clicks > 1);
        }

        private void tv_Books_MouseDown(object sender, MouseEventArgs e)
        {
            //获取鼠标点击次数
            mouseNode.Clicks = e.Clicks;
            mouseNode.X = e.X;
            mouseNode.Y = e.Y;
            mouseNode.Node = tv_Books.GetNodeAt(new Point(e.X,e.Y));
            if (mouseNode.Node == null)
            {
                return;
            }
            string[] names = mouseNode.Node.Name.Split('_');
            mouseNode.Type = (ETreeNodeKeyType)Enum.Parse(typeof(ETreeNodeKeyType), names[0]);
            mouseNode.Id = names[1];
        }

        private void 新增书籍ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mouseNode.Type.Equals(ETreeNodeKeyType.书籍))
            {
                //找出这本书籍的FolderId;
                string folderUUID = folder.Get<BookModel>(i => i.Id == mouseNode.Id).FolderId;
                FormMain.ShowFormBook(EditState.新建, folderUUID);
            }
            else
            {
                FormMain.ShowFormBook(EditState.新建, mouseNode.Id);
            }
        }

        private void 删除ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mouseNode.Type.Equals(ETreeNodeKeyType.书籍))
            {
                book.Delete(book.Find<BookModel>(mouseNode.Id));
                book.SaveChanges();
                tv_BooksRefresh();
            }
            else
            {
                folder.Delete(folder.Find<FolderModel>(mouseNode.Id));
                folder.SaveChanges();
                tv_BooksRefresh();
            }
        }

        private void 新增文件夹ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string folderName = Interaction.InputBox("请输入文件夹名称", "文件夹名称", "", 100, 100);
            if (folderName == "" || folderName == null)
            {
                MessageBox.Show("请输入正确的文件夹名称");
                return;
            }
            string folderId;
            if (mouseNode.Type.Equals(ETreeNodeKeyType.书籍))
            {
                //mouseNode.Id是书籍Id，找出这本书籍的FolderId;
                folderId = folder.Get<BookModel>(i => i.Id == mouseNode.Id).FolderId;
            }
            else
            {
                folderId = mouseNode.Id;
            }
            //生成
            folder.Add(new FolderModel
            {
                Name = folderName,
                FolderId = folderId,
            });
            folder.SaveChanges();
            tv_BooksRefresh();
        }

        private void 修改ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mouseNode.Type.Equals(ETreeNodeKeyType.书籍))
            {
                FormMain.ShowFormBook(EditState.修改, mouseNode.Id);
            }
            else
            {
                string folderName = Interaction.InputBox("请输入文件夹名称", "文件夹名称", "", 100, 100);
                if (folderName == "" || folderName == null)
                {
                    MessageBox.Show("请输入正确的文件夹名称");
                    return;
                }
                FolderModel folderModel = folder.Get<FolderModel>(m=>m.Id == mouseNode.Id);
                folder.Update(folderModel);
                folder.SaveChanges();
            }
        }

        private void tv_Books_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                //右键单击不生效
                return;
            }
            if (mouseNode.Node == null || mouseNode.X < mouseNode.Node.Bounds.Left || mouseNode.X > mouseNode.Node.Bounds.Right)
            {
                //不在区域内
                return;
            }
            //在区域内，响应处理
            if (mouseNode.Type == ETreeNodeKeyType.文件夹)
            {
                //文件夹，不做处理
                return;
            }
            else if (mouseNode.Type == ETreeNodeKeyType.书籍)
            {
                FormMain.SetReadViewBook(mouseNode.Id);
            }
            else
            {
                MessageBox.Show("异常：节点首字无法识别:" + mouseNode.Type.ToString());
            }

        }

        private void 刷新ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tv_BooksRefresh();
        }

        private void 下载ToolStripMenuItem_Click(object sender, EventArgs e)
        {

            if (mouseNode.X < mouseNode.Node.Bounds.Left || mouseNode.X > mouseNode.Node.Bounds.Right)
            {
                //不在区域内
                return;
            }
            //在区域内，响应处理
            if (mouseNode.Type == ETreeNodeKeyType.文件夹)
            {
                ThreadDownload.Start(mouseNode.Id, new FolderModel().GetType());
            }
            else if (mouseNode.Type == ETreeNodeKeyType.书籍)
            {
                ThreadDownload.Start(mouseNode.Id,new BookModel().GetType());
            }
            else
            {
                MessageBox.Show("异常：节点首字无法识别:" + mouseNode.Type.ToString());
            }
        }

        private void 同步ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mouseNode.X < mouseNode.Node.Bounds.Left || mouseNode.X > mouseNode.Node.Bounds.Right)
            {
                //不在区域内
                return;
            }
            //在区域内，响应处理
            switch (mouseNode.Type)
            {
                case ETreeNodeKeyType.文件夹:
                    var books = DownloadBackupBooks(mouseNode.Id);
                    UpdateBackupBooksAsync(books);
                    return;
                case ETreeNodeKeyType.书籍:
                    //书籍暂时不做处理
                    return;
                default:
                    throw new Exception("异常：节点首字无法识别:" + mouseNode.Type.ToString());
            }
        }
        private void UpdateBackupBooksAsync(List<BookModel> books)
        {
            var scheduler = new LimitedConcurrencyLevelTaskScheduler(3);
            Task<object>[] tasks = new Task<object>[books.Count];
            for(int i = 0; i < books.Count; i++)
            {
                var item = books[i];
                tasks[i] = Task<object>.Factory.StartNew(()=> { return backup.Put("BookModels", item); }, CancellationToken.None, TaskCreationOptions.None, scheduler);
            }
            Task.WaitAll(tasks);
        }
        private List<BookModel> DownloadBackupBooks(string folderId)
        {
            //备份功能暂不可用，先注释掉
            //Dictionary<string, DateTime> keyValuePairs = backup.Get<BookModel>("BookModels", folderId);
            Dictionary<string, DateTime> keyValuePairs = new Dictionary<string, DateTime>();
            //过滤重复项目
            var books = book.GetList<BookModel>(b => b.FolderId == folderId);
            if(keyValuePairs.Count == 0)
            {
                //网络没有数据，跳过下载，直接上传
                return books;
            }
            foreach(var keyValue in keyValuePairs)
            {
                var tmpBook = books.Where(b => b.Id == keyValue.Key).FirstOrDefault();
                if(tmpBook == null)
                {
                    //数据不存在，获取完整数据并且保存
                    tmpBook = backup.Get<BookModel>("BookModels", keyValue.Key);
                    book.Add(tmpBook);
                }
                else if(tmpBook.ActiveTime > keyValue.Value)
                {
                    //本地时间晚于网络时间，将本地数据上传
                }
                else if (tmpBook.ActiveTime < keyValue.Value)
                {
                    //本地时间早于网络时间，将网络数据下载覆盖
                    string tmpid = tmpBook.Id;
                    books.Remove(tmpBook);//删除，表示不需要上传
                    //下载最新数据覆盖本地
                    tmpBook = backup.Get<BookModel>("BookModels", keyValue.Key);
                    tmpBook.Id = tmpid;
                    book.Update(tmpBook);
                }
                else if(tmpBook.ActiveTime == keyValue.Value)
                {
                    books.Remove(tmpBook);//删除，表示不需要上传
                }
            }
            book.SaveChanges();
            return books;
        }
    }
}
